﻿using Nes.Core.APUS;
using Nes.Core.Buss;
using Nes.Core.PPUS;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Nes.Core
{
    public class NesOption
    {
        public Int32 SampleRate = 48000;
        public Action<Byte[]> OnFrame;
        public Action<Double> OnAudioSample;
    }

    public interface INes
    {
        NesOption Option { get; }
        CPU CPU { get; }
        ICartridge Cartridge { get; }
        IBus CPUBus { get; }
        void Clock();
        void Frame();
        Int32 GetFps();
        StdController Ctrl1 {  get; }
        StdController Ctrl2 {  get; }
    }

    public class Nes : INes
    {
        public NesOption Option { get; private set; }
        public CPU CPU { get; private set; }
        public PPU PPU { get; private set; }
        public DMA DMA { get; private set; }
        public APU APU { get; private set; }
        public ICartridge Cartridge { get; private set; }
        public IBus CPUBus { get; private set; }
        public IBus PPUBus { get; private set; }
        public StdController Ctrl1 { get; private set; }
        public StdController Ctrl2 { get; private set; }

        private Int32 FramePerSecond;
        private DateTime lastTime;

        public Nes(Byte[] data, NesOption option)
        {
            Cartridge = new Cartridge(data, new Byte[8192]);
            Option = PreseOption(option);
            // 卡带接到CPUBus
            var _CPUBus = new CPUBus(Cartridge);
            // CpuBus 接到CPU
            CPU = new CPU(_CPUBus);
            PPUBus = new PPUBus(Cartridge);
            
            PPU = new PPU(PPUBus, Cartridge.Mapper, Option.OnFrame, CPU);
            APU = new APU(Option.SampleRate, Option.OnAudioSample);
            APU.CPUBus = _CPUBus;
            APU.CPU = CPU;
            DMA = new DMA(CPU, PPU);
            Ctrl1 = new StdController();
            Ctrl2 = new StdController();
            _CPUBus.DMA = DMA;
            _CPUBus.PPU = PPU;
            _CPUBus.APU = APU;
            _CPUBus.Ctrl1 = Ctrl1;
            _CPUBus.Ctrl2 = Ctrl2;
            lastTime = DateTime.Now;
            FramePerSecond = 0;
            CPU.Reset();
        }

        private NesOption PreseOption(NesOption option)
        {
            if(option == null)
            {
                option = new NesOption();
            }
            if(option.SampleRate == 0)
            {
                option.SampleRate = 48000;
            }
            return option;
        }

        public void Clock()
        {
            CPU.Clock();
            APU.Clock();
            PPU.Clock();
            PPU.Clock();
            PPU.Clock();
        }

        public void Frame()
        {
            var frame = PPU.Frame;
            while(true)
            {
                Clock();
                var newFrame = PPU.Frame;
                if (frame != newFrame)
                {
                    FramePerSecond++;
                    break;
                }
            }
        }

        public Int32 GetFps()
        {
            var now = DateTime.Now;
            var count = FramePerSecond;
            var dltTime = (now - lastTime).TotalMilliseconds / 1000;
            FramePerSecond = 0;
            lastTime = now;

            var val = (Int32)(count / dltTime);
            return val;
        }
    }
}
